﻿using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;

using VSTalk.Engine.Core.Context;
using VSTalk.Engine.Core.EnvironmentManagers;
using VSTalk.Engine.Core.EnvironmentManagers.UI;
using VSTalk.Engine.Core.Feed;
using VSTalk.Engine.Utils;
using VSTalk.Tools;
using VSTalk.Model;

namespace VSTalk.Engine.ViewModel.Messenger
{
    public class ChatFrameViewModel : IFrameViewModel, IListener
    {
        public static IEnvDataManager EnvDataManager
        {
            get { return AppServices.Get<IEnvDataManager>(); }
        }

        public static IModelContext ModelContext
        {
            get { return AppServices.Get<IModelContext>(); }
        }

        private static IWindowManager WindowManager
        {
            get { return AppServices.Get<IWindowManager>(); }
        }

        private static IEnvWindowsManager EnvWindowsManager
        {
            get { return WindowManager.EnvWindowsManager; }
        }

        public ObservableCollection<IChatViewModel> ActiveChats { get; set; }

        private IChatViewModel _currentChat;

        private ISubscription _activeChatUnit;
        private ISubscription _interlocutorUnit;
        private ISubscription _roomUnit;

        public RelayCommand SendMessage { get; private set; }

        public RelayCommand ShowMainControl { get; private set; }

        public RelayCommand PasteCallStack { get; private set; }

        public RelayCommand PasteActiveDocument { get; private set; }

        public RelayCommand PasteDebugOutput { get; private set; }

        public bool IsActive
        {
            get { return EnvWindowsManager.ContextIsOnScreen(this); }
        }

        public RelayCommand<IChatViewModel> FinishConversation { get; private set; }

        public bool HasActiveChats
        {
            get { return ActiveChats.Any(); }
        }

        public IChatViewModel CurrentChat
        {
            get { return _currentChat; }
            set
            {
                _currentChat = value;
                PropertyChanged.Notify(() => CurrentChat);
            }
        }

        private IMessageFeed MessageFeed
        {
            get { return AppServices.Get<IMessageFeed>(); }
        }

        public ChatFrameViewModel()
        {
            SetCommands();
            ActiveChats = new ObservableCollection<IChatViewModel>();
        }

        public void StartListen()
        {
            _activeChatUnit = ActiveChats.HandleAnyChanges(() => PropertyChanged.Notify(() => HasActiveChats));
            _interlocutorUnit = ModelContext.Interlocutors.HandleRemoveChanges(removed: interlocutor =>
            {
                var target = ActiveChats.FirstOrDefault(viewModel => viewModel.Model == interlocutor);
                if (target != null)
                {
                    FinishConversationExecuted(target);
                }
            });
            _roomUnit = ModelContext.Rooms.HandleRemoveChanges(removed: room =>
            {
                var target = ActiveChats.FirstOrDefault(viewModel => viewModel.Model == room);
                if (target != null)
                {
                    FinishConversationExecuted(target);
                }
            });

        }

        public void StopListen()
        {
            _activeChatUnit.Unsubscribe();
            _interlocutorUnit.Unsubscribe();
            _roomUnit.Unsubscribe();
        }

        private void SetCommands()
        {
            ShowMainControl = new RelayCommand(ShowMainWindowExecuted);
            PasteCallStack = new RelayCommand(PasteCallStackExecuted);
            PasteActiveDocument = new RelayCommand(PasteActiveDocumentExecuted);
            PasteDebugOutput = new RelayCommand(PasteDebugOutputExecuted);

            SendMessage = new RelayCommand(SendMessageExecuted, SendMessageCanExecute);

            FinishConversation = new RelayCommand<IChatViewModel>(FinishConversationExecuted);
        }

        private void ShowMainWindowExecuted()
        {
            WindowManager.ShowMainWindow();
        }
        protected void PasteDebugOutputExecuted()
        {
            var doc = EnvDataManager.GetDebugOutput();
            if (string.IsNullOrEmpty(doc)) return;
            CurrentChat.AppendToMessage(string.Format("========Debug Output========\n{0}", doc));
        }

        protected void PasteActiveDocumentExecuted()
        {
            var doc = EnvDataManager
                .GetActiveDocument();
            if (string.IsNullOrEmpty(doc)) return;
            CurrentChat.AppendToMessage(string.Format("========Active Document========\n{0}", doc));
        }

        protected void PasteCallStackExecuted()
        {
            var currentStack = EnvDataManager
                .GetCallStack()
                .Reverse()
                .ToList();

            if (currentStack.Count() == 0) return;
            var sb = new StringBuilder();
            currentStack.Each(frame => sb.Append(frame + "\n"));
            CurrentChat.AppendToMessage(string.Format("\n========Call Stack========\n{0}", sb));
        }

        protected void FinishConversationExecuted(IChatViewModel interlocutorModel)
        {
            interlocutorModel.StopListen();
            MessageFeed.Unsubscribe(interlocutorModel);
            ActiveChats.Remove(interlocutorModel);
            CurrentChat = ActiveChats.FirstOrDefault();
        }

        protected void SendMessageExecuted()
        {
            CurrentChat.SendMessage();
        }

        protected bool SendMessageCanExecute()
        {
            return CurrentChat != null && CurrentChat.CanSendMessage();
        }

        public void AttachEntity(Interlocutor interlocutor)
        {
            var activeChat = ActiveChats.FirstOrDefault(contact => contact.Model == interlocutor);
            if (activeChat == null)
            {
                activeChat = new InterlocutorChatViewModel(this, interlocutor);
                MessageFeed.Subscribe(activeChat);
                activeChat.StartListen();
                ActiveChats.Add(activeChat);
            }
            CurrentChat = activeChat;
        }

        public void AttachEntity(Room room)
        {
            var activeChat = ActiveChats.FirstOrDefault(contact => contact.Model == room);
            if (activeChat == null)
            {
                activeChat = new RoomChatViewModel(this, room);
                MessageFeed.Subscribe(activeChat);
                activeChat.StartListen();
                ActiveChats.Add(activeChat);
            }
            CurrentChat = activeChat;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}
